---
title: 计算机体系结构
description: 体系结构分类（Flynn）、指令系统
---
## 体系结构分类

### 按处理机的数量进行分类

单处理系统（一个处理单元和其它设备集成）、并行处理系统（两个以上的处理机互联）、分布式处理系统（物理上远距离且松耦合的多计算机系统）

### <RedSpan>Flynn 分类法</RedSpan>
分类有两个因素，即<RedSpan>指令流和数据流</RedSpan>。

<RedSpan>指令流由控制部分处理</RedSpan>，每一个控制部分处理一条指令流，多指令流就有多个控制部分；<br/>
<RedSpan>数据流由处理器来处理</RedSpan>，每一个处理器处理一条数据流，多数据流就有多个处理器；<br/>
<RedSpan>至于主存模块，是用来存储的，存储指令流或者数据流</RedSpan>，因此，无论是多指令流还是多数据流，都需要多个主存模块来存储，对于主存模块，指令和数据都是一样的。


| 体系结构类型        | 结构                                 | 关键特性                | 代表                               |
|---------------|------------------------------------|---------------------|----------------------------------|
| 单指令流单数据流 SISD | 控制部分：1个 <br/> 处理器：1个 <br/> 主存模块：1个 |                     | 单处理器系统                           |
| 单指令流多数据流 SIMD | 控制部分：1个 <br/> 处理器：n个 <br/> 主存模块：n个 | 各处理器以异步的形式执行同一条指令   | 并行处理机器 <br/> 阵列处理机 <br/> 超级向量处理机 |
| 多指令流单数据流 MISD | 控制部分：n个 <br/> 处理器：1个 <br/> 主存模块：n个 | 被证明不可能，至少是不实际       | 目前没有，有文献称 <br/>流水线计算机为此类               |
| 多指令流多数据流 MIMD | 控制部分：n个 <br/> 处理器：n个 <br/> 主存模块：n个 | 能够实现作业、任务、指令等各级全面并行 | 多处理机系统 <br/> 多计算机                |


依据计算机特性，是<RedSpan>由指令来控制数据的传输</RedSpan>，因此，一条指令可以控制一条或多条数据流，但<RedSpan>一条数据流不能被多条指令控制</RedSpan>，否则会出错，就如同上级命令太多还互相冲突，不知道执行那个，因此 <RedSpan>多指令流单数据流 MISD 不可能实现。</RedSpan>

## 指令系统

一条指令由<RedSpan>操作码和操作数</RedSpan>两部分组成，操作码<RedSpan>决定了要完成的操作</RedSpan>，操作数指<RedSpan>参加运算的地址及其所在的单元地址</RedSpan>。

在计算机中，操作要求和操作数地址都由二进制数码表示，分别称为操作码和地址码，整条指令以二进制编码的形式存放在存储器中。

计算机指令执行过程： <RedSpan>取指令 $\Rightarrow$ 分析指令 $\Rightarrow$ 执行指令三个步骤</RedSpan>，首先将程序计数器 PC 中的指令地址取出，送入地址总线，CPU 依据指令地址去内存中取出指令内容后存入指令寄存器 IR；而后由指令译码器 ID 进行分析，分析指令操作码；最后执行指令，取出指令执行所需的源操作数。

### 指令寻址方式
指令寻址方式是指，执行完这一条指令之后，如何找到下一条指令。
* <RedSpan>顺序寻址方式</RedSpan>：当执行一段程序时，一条指令接着一条指令地顺序执行（指令的地址在 PC 中）。
* <RedSpan>跳跃寻址方式</RedSpan>： <RedSpan>下一条指令的地址不是由程序计数器给出，而是由本条指令直接给出</RedSpan>。程序跳跃后，按新的指令地址开始顺序执行。因此，程序计数器的内容也必须相应改变，以便及时跟踪新的指令地址。


### 指令操作数的寻址方式
指令操作数的寻址方式是用来找操作数的，指令一般存放在主存中 ，操作数可能是主存、也可能是寄存器。

* <RedSpan>立即寻址方式</RedSpan>：指令的地址码字段指出的不是地址，而是操作数本身。
* <RedSpan>直接寻址方式</RedSpan>：在指令的地址字段中直接指出操作数在主存中的地址。
* <RedSpan>间接寻址方式</RedSpan>：指令地址码字段所指向的存储单元中存储的操作数的地址。
* <RedSpan>寄存器寻址方式</RedSpan>：指令中的地址码的内存是寄存器的编号。
* <RedSpan>基址寻址方式</RedSpan>： 将基址寄存器的内容加上指令中的形式地址而形成的操作数的有效地址，其优点是扩大寻址能力。
* <RedSpan>变址寻址方式</RedSpan>： 变址寻址方式计算有效地址的方法与基址寻址方式很相似，它是将变址寄存器的内容加上指令中的形式地址而形成操作数的有效地址。

### 指令系统分类

CISC 是 <RedSpan>复杂指令系统</RedSpan>，兼容性强、指令繁多、长度可变、由微程序实现；

RISC 是 <RedSpan>精简指令系统</RedSpan>，指令少，使用频率接近，主要依靠硬件实现（通用寄存器、硬布线逻辑控制）；


| 指令系统类型   | 指令                                                           | 寻址方式  | 实现方式                                 | 其它                 |
|----------|--------------------------------------------------------------|-------|--------------------------------------|--------------------|
| CISC（复杂） | 数量多，使用频率差别很大，<br/>可变长格式                                      | 支持多种  | 微程序技术（微码）                            | 研制周期长              |
| RISC（精简） | 数量少，使用频率接近，<br/>定长格式，大部分分为单周期指令,<br/>操作寄存器，只有 Load/Store 操作内存 | 支持方式少 | 增加了通用寄存器；<br/>硬布线逻辑控制为主；<br/>适合采用流水线 | 优化编译，有效<br/>支持高级语言 |



### 指令流水线

<img src="https://wkq-img.oss-cn-chengdu.aliyuncs.com/20241019211355.png"/>

指令流水线将<RedSpan>指令分成不同段，每段由不同的部分去处理</RedSpan>，因此可以产生叠加的效果，所有的部件去处理指令的不同段。

#### RISC 中的流水线技术

* <RedSpan>超流水线（Super Pipe Line ）技术</RedSpan>。它通过细化流水线、增加级数和提高主频，使得在每个机器周期内能完成一个甚至两个浮点数操作。其实质是 <RedSpan>以时间换取空间</RedSpan>
* <RedSpan>超标量（Super Scalar）技术</RedSpan>。它通过内装多条流水线来同时执行多个处理，其时钟频率虽然与一般流水线接近，却又更小的 CPI。其实质是 <RedSpan>以空间换取时间</RedSpan>。
* <RedSpan>超长指令字技术</RedSpan>：VLIW 和超标量都是 20 世纪 80 年代出现的概念，其共同特点是要同时执行多条指令，其不同在于超标量依赖硬件来实现并行处理的调度，VLIW 则充分发挥软件的作用，而使硬件简化，性能提高。

:::tip
RISC 指令集中才使用了流水线技术，SISC 指令集中没有使用流水线技术。
:::

#### 流水线时间计算
<RedSpan>流水线周期</RedSpan>：指令分成不同执行段，其中执行时间最长的段为流水线周期。

<RedSpan>流水线执行时间</RedSpan>：1 条指令总执行时间 + (总指令条数-1) $\times$ 流水线周期。

<RedSpan>流水线吞吐率计算</RedSpan>：吞吐率即单位时间内执行的指令条数。<RedSpan>公式为： 指令条数 $\div$ 流水线执行时间</RedSpan>

<RedSpan>流水线加速比计算</RedSpan>：加速比即使用流水线之后的效率提升度，即比不使用流水线快了多少倍，越高表明流水线效率越高。<RedSpan>公式：不使用流水线的执行时间 $\div$ 使用流水线执行时间</RedSpan>


:::tip
毫秒是千分之一秒，微秒是千分之一毫秒，纳秒是千分之一微秒数。
:::

## 存储系统
<img src="https://wkq-img.oss-cn-chengdu.aliyuncs.com/20241019220025.png"/>

计算机采用分级存储体系的主要目的是为了解决存储容量、成本和速度之间的矛盾关系。

分为两级存储：Cache $\Leftrightarrow$ 主存、主存 $\Leftrightarrow$ 辅存（虚拟存储体系）

<RedSpan>局部性原理</RedSpan>：总的来说，在 CPU 运行时，所访问的数据会趋向于一个较小的局部空间地址内，包括下面两个方面：
* 时间局部性原理：如果一个数据项正在被访问，那么在近期它很可能会被再次访问，即在<RedSpan>相邻的时间里会访问同一个数据项</RedSpan>。
* 空间局部性原理：在最近的将来会用到的数据的地址和现在正在访问的数据地址很可能是相近的，即<RedSpan>相邻的空间地址会被连续访问</RedSpan>。（最常见，比如循环）

### Cache
高速缓冲 Cache 用来存储当前<RedSpan>最活跃的程序和数据，直接与 CPU 交互，位于 CPU 和主存之间，</RedSpan>容量小，速度为内存的 5-10 倍，由半导体材料构成。其内容是主存内存的副本拷贝，对于程序员来说是透明的。


Cache 由 <RedSpan>控制部分和存储器组成</RedSpan>，存储器存储数据，控制部分判断 CPU 要访问的数据是否在 Cache 中，在则命中，不在则依据一定的算法从内存中替换。

### 地址映射
在 CPU 工作时，<RedSpan>送出的是主存单元的地址，而应从 Cache 存储器中读/写信息</RedSpan>。这就需要<RedSpan>将主存地址转换为 Cache 存储器地址</RedSpan>，这种地址的转换称为地址映像，由<RedSpan>**硬件自动完成**</RedSpan>，分为下列三种方法。

#### 直接相连
将 <RedSpan>Cache 存储器等分成块，主存也等分成块并编号</RedSpan>。主存中的块与 Cache 中的块的对应关系是固定的，也即 <RedSpan>二者块号相同才能命中</RedSpan>。地址变换简单但不灵活，容易造成资源浪费。

<img src="https://wkq-img.oss-cn-chengdu.aliyuncs.com/20241019221832.png"/>


主存比较大，划分为多个区，每个区都和 Cache 一样大，每个区的块号都和 Cache 一致。

#### 全相连映射
<img src="https://wkq-img.oss-cn-chengdu.aliyuncs.com/20241019222204.png"/>
同样都等分成块并编号。<RedSpan>主存中任意一块都与 Cache 中任意一块对应</RedSpan>。因此 <RedSpan>可以随意调入 Cache 任意位置</RedSpan>，但地址变换复杂，速度较慢。因为主存可以随意调入 Cache 任意块，只有当 Cache 满了才会发生块冲突，<RedSpan>是最不容易发生块冲突的映射方式。</RedSpan>


:::tip
全相连映射主存没有分区，每一块的大小和 Cache 中的块大小相同，不限制必须块号相同。
:::

#### 组组相邻映射

<RedSpan>前面两种方式的结合</RedSpan>，将 Cache 存储器先分块再分组，主存中也同样先分块再分组，<RedSpan>组间采用直接映像，即主存中的组号与 Cache 中组号相同的组才能命中，但是，组内全相连映像，也即组号相同的两个组内的所有块可以任意调换。</RedSpan>

### 替换算法
为了便于 CPU 访问，要把主存中经常访问的内容调入 Cache 中去。当 Cache 满了之后，就需要替换掉不常用的块。

替换算法的目标就是使<RedSpan>Cache 获得尽可能高的命中率</RedSpan>。常用的替换算法有如下几种。
1. <RedSpan>随机替换算法</RedSpan>：就是用随机数发生器产生一个要替换的块号，将该块替换出去。
2. <RedSpan>先进先出算法</RedSpan>：就是将最先进入 Cache 的信息块替换出去。
3. <RedSpan>最近最少使用算法（最常用）</RedSpan>：这种方法是将近期最少使用的 Cache 中的信息块替换出去。
4. <RedSpan>优化替换算法</RedSpan>：这种方法必须先执行一次程序，统计 Cache 的替换情况。有了这样的先验信息，在第二次执行该程序时就可以用最有效的方法来替换。

### 命中率和平均时间
Cache 有一个命中率的概念，即 <RedSpan>当 CPU 所访问的数据在 Cache 中时，命中，直接从 Cache 中读取数据</RedSpan>，设读取一次 Cache 的时间为 1ns，若 CPU 访问的数据 <RedSpan>不在 Cache 中，则需要从内存中读取，</RedSpan>设读取一次内存的时间为 1000ns，若在 CPU 多次读取数据过程中，有 90% 命中 Cache，则 CPU 读取一次的平均时间为 $(90\% \times 1 + 10\% \times 1000)ns$


<img alt={'Cache 的容量和命中率的关系'} src="https://wkq-img.oss-cn-chengdu.aliyuncs.com/20241019235858.png"/>


### 主存编址
**基本概念**：<RedSpan>K、M、G 是数量单位，在存储器里面相差 1024 倍</RedSpan> 。

例子：地址编号从 80000H 到 BFFFFH 按字节编制的内存容量为 （256） KB，若用 $16K \times 4$ bit 的存储器芯片构成该内存，共需 （32） 片。

> 首先计算出地址段包含的存储空间数，为 BFFFFH - 80000H + 1 = 40000H, 按字节编址，即一个存储空间占一个字节，共 40000H 个字节，即 256 KB；该存储芯片总容量为 8KB, 因此需要 32 片。


:::tip
不要硬算，要化简成 2 的幂指数来算。
:::


### 磁盘结构和参数
磁盘有 <RedSpan>正反两个盘面，每个盘面有多个同心圆，每个同心圆是一个磁道，每个同心圆又被划分为多个扇区，数据就被存放在一个个扇区中。</RedSpan>

<RedSpan>磁头首先要寻找到对应的磁道</RedSpan>，然后等待磁盘进行<RedSpan>周期旋转，旋转到指定的扇区，才能读取到对应的数据</RedSpan>，因此，会产生 <RedSpan>寻道时间和等待时间</RedSpan>。公式为： 存取时间 = 寻道时间 + 等待时间（平均定位时间+转动延迟）

:::tip
寻道时间是磁头移动到磁道所需的时间；等待时间为等待读写的扇区转到磁头下方所用的时间。
:::
### 磁盘调度算法
磁盘数据的读取分为寻道时间 + 旋转时间，也即先找到对应的磁道，而后再旋转到对应的扇区才能读取数据，其中 <RedSpan>寻道时间耗时最长</RedSpan>，需要重点调度，有如下调度算法：

1. <RedSpan>先来先服务FCFS</RedSpan>：跟进进程请求访问磁盘的先后顺序进行调度。
2. <RedSpan>最短寻道时间优先SSTF</RedSpan>: 请求访问的磁道与当前磁道最近的进程优先调度，使得每次的寻道时间最短。会产生“饥饿现象“，即远处进程可能永远无法访问。
3. <RedSpan>扫描算法SCAN</RedSpan>：又称电梯算法，磁头在磁盘上双向移动，其会选择离磁头当前所在磁道最近的请求访问的磁道，并且与磁头移动方向一致，磁头永远都是从里向外或者从外向里一直移动完才掉头，与电梯类似。
4. <RedSpan>单向扫描调度算法CSCAN</RedSpan>：与SCAN不同的是，其只做单向移动，即只能从里向外或者从外向里。